Track inbound payments by PaymentId - #948
Conversation
|
👋 Thanks for assigning @tankyleo as a reviewer! |
51dba72 to
ff3345d
Compare
ff3345d to
544e100
Compare
544e100 to
4c3f2e4
Compare
4c3f2e4 to
4a24ee7
Compare
4a24ee7 to
d1b5ffb
Compare
|
Rebased without further changes for now. |
d1b5ffb to
b1f6564
Compare
|
Force-pushed some fixes: > git diff-tree -U2 d1b5ffb3 b1f65649
diff --git a/bindings/python/src/ldk_node/test_ldk_node.py b/bindings/python/src/ldk_node/test_ldk_node.py
index 304caf9c..063c95f7 100644
--- a/bindings/python/src/ldk_node/test_ldk_node.py
+++ b/bindings/python/src/ldk_node/test_ldk_node.py
@@ -236,5 +236,5 @@ class TestLdkNode(unittest.TestCase):
sender_payment = node_1.payment(keysend_payment_id)
- receiver_payment = node_2.payment(keysend_payment_id)
+ receiver_payment = node_2.payment(received_event.payment_id)
self.assertIsNotNone(sender_payment)
diff --git a/tests/integration_tests_migration.rs b/tests/integration_tests_migration.rs
index c4e63451..59621476 100644
--- a/tests/integration_tests_migration.rs
+++ b/tests/integration_tests_migration.rs
@@ -204,5 +204,5 @@ async fn migrate_node_across_all_backends() {
let invoice = node_b.bolt11_payment().receive(10_000, &description.into(), 3600).unwrap();
let ln_send_id = node.bolt11_payment().send(&invoice, None).unwrap();
- expect_payment_successful_event!(node, Some(ln_send_id), None);
+ expect_payment_successful_event!(node, ln_send_id, None);
expect_payment_received_event!(node_b, 10_000);
@@ -212,5 +212,5 @@ async fn migrate_node_across_all_backends() {
let invoice = node.bolt11_payment().receive(5_000, &description.into(), 3600).unwrap();
let ln_receive_id = node_b.bolt11_payment().send(&invoice, None).unwrap();
- expect_payment_successful_event!(node_b, Some(ln_receive_id), None);
+ expect_payment_successful_event!(node_b, ln_receive_id, None);
expect_payment_received_event!(node, 5_000);
|
046355b to
452c643
Compare
|
Looking at this through the lens of self-imposed versus intrinsic complexity, I think this PR again makes the cost of our current architecture clear. LDK and ldk-node persist related state independently, without a consistent commit boundary, so we have to recover consistency through careful ordering, replay, cleanup, and expiry logic. |
tankyleo
left a comment
There was a problem hiding this comment.
A few nits feel free to squash will take a better pass tomorrow
The switch to tracking payments by ID happened with LDK Node v0.3.0, which is >1.5 years old by now. We can be pretty certain that nobody is upgrading from an older version to the upcoming v0.8. Here we hence make the `payment_id` fields in `Event` required which is a nice API simplification that will also be utilized in the next commit. Co-Authored-By: HAL 9000
452c643 to
f96d289
Compare
Gate manual claiming for unknown custom-hash BOLT11 payments behind a config flag. Nodes that do not opt in fail these payments back without storing or queueing user events. Co-Authored-By: HAL 9000
f96d289 to
748143c
Compare
| /// | ||
| /// **Warning:** Enabling this may let any holder of a valid invoice generated by this node tie | ||
| /// up inbound HTLC slots and grow the persisted event queue until the payment is claimed, | ||
| /// failed, or times out. |
There was a problem hiding this comment.
I think it's worth highlighting that this could happen after the payment got claimed. In that case, the payment should be failed back instead of claimed twice.
Perhaps even suggest that the user should keep state tracking all pending payment hashes, and fail any payment hashes not in the pending list ?
There was a problem hiding this comment.
Adjusted docs in fixup.
There was a problem hiding this comment.
Don't yet see a note about this here do you think it's not worth it ?
There was a problem hiding this comment.
Don't yet see a note about this here do you think it's not worth it ?
Hmm, we generally have the guidance that users that enable this need to handle PaymentClaimable and fail anything they're not going to claim? Or maybe I'm confused on what specifically you want mentioned?
| /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. Enabling this may let any | ||
| /// holder of a valid invoice generated by this node tie up inbound HTLC slots and grow the | ||
| /// persisted event queue until the payment is claimed, failed, or times out. |
There was a problem hiding this comment.
I'd add some language similar to this after this section:
"This event could be emitted multiple times for a single hash, including well after the invoice got claimed, in case this invoice gets paid multiple times. Users should claim the first, and fail the rest."
There was a problem hiding this comment.
Adjusted docs in fixup.
There was a problem hiding this comment.
Likewise here, do you find it's not necessary ?
There was a problem hiding this comment.
See above: users should generally fail anything they're not going to claim. Whether or not they claim doubly-sent payments is a bit up to them, though it's of course nicer not to do so.
Note that if we give this guidance we probably also want to implement this for LDK Server, which would mean yet-another persisted store, meaning we should then probably still do it in LDK Node if we deem it that important. But we just switched away from making this PR overly complex with all the secondary indexes etc. 😬
Any case, I now added a note.
There was a problem hiding this comment.
Hmm ok to confirm the plan in ldk-server would be to claim all the hashes we now the preimage for, regardless of duplicates ?
My intuition there says the responsibility is on the sender of the payment to not pay an invoice twice, so seems good to me.
There was a problem hiding this comment.
Note that if we give this guidance we probably also want to implement this for LDK Server, which would mean yet-another persisted store, meaning we should then probably still do it in LDK Node if we deem it that important. But we just switched away from making this PR overly complex with all the secondary indexes etc. 😬
Perhaps then we should drop the "should be failed back" note, and just clarify things like this:
"If the same payment hash is paid more than once, claiming each event accepts each payment"
There was a problem hiding this comment.
Hmm ok to confirm the plan in ldk-server would be to claim all the hashes we now the preimage for, regardless of duplicates ?
No, the plan would be to either not expose PaymentClaimable at all, or, if we do, still have it be the user's responsibility to handle the events.
My intuition there says the responsibility is on the sender of the payment to not pay an invoice twice, so seems good to me.
Yeah, it is, but it's also not great to knowingly steal money, even if the other party offers it through a mistake. So preferably we would have default behavior that avoids it, though in this case I'm not sure if it's worth the complication.
Use a name that reflects both possible user actions for unknown BOLT11 payments: either claim them with a preimage or fail them back. Co-Authored-By: HAL 9000
Create inbound BOLT11 records from claimable and claimed events so inbound payments can be tracked by the IDs emitted by LDK. Generate outbound BOLT11 IDs from KeysManager entropy instead of deriving them from the payment hash. Co-Authored-By: HAL 9000
Keep outbound BOLT11 payments keyed by payment hash so repeated attempts to pay the same invoice can still return DuplicatePayment. Co-Authored-By: HAL 9000
Clarify that v0.7-or-earlier for-hash invoices can still emit PaymentClaimable after upgrade regardless of the new config flag. Co-Authored-By: HAL 9000
748143c to
97b27d4
Compare
| return (payment_id, Some(info)); | ||
| } | ||
|
|
||
| if legacy_id != payment_id { |
There was a problem hiding this comment.
Codex:
- [P2] Migrate current refund records to the event payment ID _ /home/ubuntu/ldk-node/src/event.rs:691-694
When request_refund_payment is used, it still inserts the inbound refund at PaymentId(payment_hash.0) (src/payment/bolt12.rs:444-464). This fallback therefore treats a current record as legacy and returns the hash-derived ID, so refund PaymentReceived events and store entries never use LDK's
unique inbound ID; repeated refund payments also collapse onto the same record. Restrict the fallback to genuinely legacy rows or re-key this record when the event arrives.
Should we drop inserting the payment into the store on request_refund_for_payment ?
There was a problem hiding this comment.
Hmm, good point. Issue is that we can't easily drop the insertion in request_refund_for_payment as otherwise we lose payer_note and quantity for inbound payments (the case where at least the former matters). Let's discuss offline whether we could carry them in Bolt12RefundContext.
| /// | ||
| /// **Warning:** Enabling this may let any holder of a valid invoice generated by this node tie | ||
| /// up inbound HTLC slots and grow the persisted event queue until the payment is claimed, | ||
| /// failed, or times out. |
There was a problem hiding this comment.
Don't yet see a note about this here do you think it's not worth it ?
| /// [`Config::manually_claim_unknown_bolt11_payments`] is enabled. Enabling this may let any | ||
| /// holder of a valid invoice generated by this node tie up inbound HTLC slots and grow the | ||
| /// persisted event queue until the payment is claimed, failed, or times out. |
There was a problem hiding this comment.
Likewise here, do you find it's not necessary ?
Clarify that claiming duplicate PaymentClaimable events accepts each payment, so unexpected duplicate or late payments should be failed. Co-Authored-By: HAL 9000
Cover manually claiming and failing BOLT11 for-hash invoices that were created before upgrading, so the new ID-based APIs keep handling legacy payment-hash IDs. Co-Authored-By: HAL 9000
Avoid adding a direct test dependency for legacy payment hash types when the v0.7 ldk-node crate already re-exports the exact crate used by its API. Co-Authored-By: HAL 9000
97b27d4 to
1f76c18
Compare
|
Thanks feel free to squash the existing fixups |
Closes #298.
This finally switches our inbound payments over to be fully tracked by payment id, and decouples payment IDs from payment hashes.